package com.atguigu.gmall.order.service.impl;

import com.alibaba.fastjson.JSON;
import com.atguigu.gmall.cart.client.CartFeignClient;
import com.atguigu.gmall.cart.model.CartInfo;
import com.atguigu.gmall.common.constant.MqConst;
import com.atguigu.gmall.common.constant.RedisConst;
import com.atguigu.gmall.common.service.RabbitService;
import com.atguigu.gmall.common.util.HttpClientUtil;
import com.atguigu.gmall.enums.model.OrderStatus;
import com.atguigu.gmall.enums.model.ProcessStatus;
import com.atguigu.gmall.order.mapper.OrderInfoMapper;
import com.atguigu.gmall.order.model.OrderDetail;
import com.atguigu.gmall.order.model.OrderInfo;
import com.atguigu.gmall.order.service.OrderDetailService;
import com.atguigu.gmall.order.service.OrderInfoService;
import com.atguigu.gmall.product.client.ProductFeignClient;
import com.atguigu.gmall.user.client.UserFeignClient;
import com.atguigu.gmall.user.model.UserAddress;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.BoundHashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 订单表 订单表 业务实现类
 * @author atguigu
 * @since 2023-05-07
 */
@Service
@SuppressWarnings("all")
public class OrderInfoServiceImpl extends ServiceImpl<OrderInfoMapper, OrderInfo> implements OrderInfoService {

    @Autowired
    private UserFeignClient userFeignClient;

    @Autowired
    private CartFeignClient cartFeignClient;

    @Autowired
    private OrderDetailService orderDetailService;

    @Autowired
    private ProductFeignClient productFeignClient;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private RabbitService rabbitService;

    @Value("${ware.url}")
    private String wareUrl;

    /**
     * 渲染用户订单确认页面相关参数
     * @param userId
     * @return
     */
    @Override
    public Map<String, Object> tradeDatas(Long userId) {
        HashMap<String, Object> mapResult = new HashMap<>();
        //调用用户微服务获取收件地址列表
        List<UserAddress> userAddressList = userFeignClient.getUserAddressListByUserId(userId);
        if (!CollectionUtils.isEmpty(userAddressList)) {
            mapResult.put("userAddressList", userAddressList);
        }
        //调用购物车微服务获取需要结算商品
        List<CartInfo> cartCheckedList = cartFeignClient.getCartCheckedList(userId);
        if (!CollectionUtils.isEmpty(cartCheckedList)) {
            //将集合泛型从购物车转为订单明细类型
            List<OrderDetail> orderDetailList = cartCheckedList.stream().map(cartInfo -> {
                OrderDetail orderDetail = new OrderDetail();
                BeanUtils.copyProperties(cartInfo, orderDetail);
                //单独设置价格属性
                orderDetail.setOrderPrice(cartInfo.getSkuPrice());
                return orderDetail;
            }).collect(Collectors.toList());
            mapResult.put("detailArrayList", orderDetailList);
            //封装${totalNum}订单商品数量
            mapResult.put("totalNum", orderDetailList.size());
            //${totalAmount}:订单总金额
            OrderInfo orderInfo = new OrderInfo();
            orderInfo.setOrderDetailList(orderDetailList);
            orderInfo.sumTotalAmount();
            mapResult.put("totalAmount", orderInfo.getTotalAmount());
        }
        //生成流水号
        String tradeNo = this.generateTradeNo(userId.toString());
        mapResult.put("tradeNo", tradeNo);
        return mapResult;
    }

    /**
     * 提交订单
     * @param orderInfo
     * @param tradeNo 用户提交流水号
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long submitOrder(OrderInfo orderInfo, String tradeNo) {
        //避免用户无意使用浏览器回退导致重复提交
        //Boolean flag = checkTradeNo(orderInfo.getUserId() + "", tradeNo);
        //if (!flag) {
        //    throw new RuntimeException("请勿重复提交订单!");
        //}
        ////将流水号删除
        //deleteTradeNo(orderInfo.getUserId().toString());

        //采用lua脚本保证判断流水号 删除流水号原子性
        String luaScriptText = "if redis.call(\"get\",KEYS[1]) == ARGV[1]\n" +
                "then\n" +
                "    return redis.call(\"del\",KEYS[1])\n" +
                "else\n" +
                "    return 0\n" +
                "end";
        DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();
        redisScript.setScriptText(luaScriptText);
        redisScript.setResultType(Long.class);
        String tradeKey = "tradeNo:" + orderInfo.getUserId();
        Long flag = (Long) redisTemplate.execute(redisScript, Arrays.asList(tradeKey), tradeNo);
        if (flag.intValue() == 0) {
            throw new RuntimeException("请勿重复提交订单!");
        }
        //todo 验证商品库存是否充足(远程调用第三方库存系统)
        //todo 验证订单中商品价格是否为发生变化(远程调用商品服务获取)
            //获取订单中所有订单商品明细 遍历商品 验证库存 验证价格
        List<OrderDetail> orderDetailList = orderInfo.getOrderDetailList();
        if (!CollectionUtils.isEmpty(orderDetailList)) {
            for (OrderDetail orderDetail : orderDetailList) {
                //远程调用第三方库存系统获取库存是否冲突
                String skuWareUrl = wareUrl + "/hasStock?skuId=" + orderDetail.getSkuId() + "&num=" + orderDetail.getSkuNum();
                String hashStock = HttpClientUtil.doGet(skuWareUrl);
                if (hashStock.equals("0")) {
                    throw new RuntimeException("商品库存不足");
                }
                //远程调用商品微服务获取商品价格是否为最新
                BigDecimal skuPrice = productFeignClient.getSkuPrice(orderDetail.getSkuId());
                if (skuPrice.compareTo(orderDetail.getOrderPrice()) != 0) {
                    //更新当前用户购物车中缓存价格更新为最新
                    String cartKey = RedisConst.USER_KEY_PREFIX + orderInfo.getUserId() + RedisConst.USER_CART_KEY_SUFFIX;
                    BoundHashOperations<String, String, CartInfo> hashOps = redisTemplate.boundHashOps(cartKey);
                    CartInfo cartInfo = hashOps.get(orderDetail.getSkuId().toString());
                    if (cartInfo != null) {
                        cartInfo.setSkuPrice(skuPrice);
                        hashOps.put(orderDetail.getSkuId().toString(), cartInfo);
                    }
                    throw new RuntimeException("订单中商品价格失效!");
                }
            }
        }
        //保存订单
        Long orderId = saveOrderInfo(orderInfo);
        //5.发送延迟关闭订单消息到RabbitMQ 通知 "订单微服务" 验证订单支付状态 修改订单状态
        rabbitService.sendDelayMessage(MqConst.EXCHANGE_DIRECT_ORDER_CANCEL, MqConst.ROUTING_ORDER_CANCEL, orderId, 300);
        return orderId;
    }

    /**
     * 为生成订单确认页面中流水号
     * @param userId
     * @return
     */
    @Override
    public String generateTradeNo(String userId) {
        String tradeNo = UUID.randomUUID().toString().replaceAll("-", "");
        String tradeKey = "tradeNo:" + userId;
        redisTemplate.opsForValue().set(tradeKey, tradeNo);
        return tradeNo;
    }

    /**
     * 验证用户提交流水号,跟redis中流水号是否一致
     * @param userId
     * @param tradeNo 客户端提交流水号
     * @return
     */
    @Override
    public Boolean checkTradeNo(String userId, String tradeNo) {
        String tradeKey = "tradeNo:" + userId;
        String redisTradeNo = (String) redisTemplate.opsForValue().get(tradeKey);
        return tradeNo.equals(redisTradeNo);
    }

    /**
     * 删除流水号
     * @param userId
     */
    @Override
    public void deleteTradeNo(String userId) {
        String tradeKey = "tradeNo:" + userId;
        redisTemplate.delete(tradeKey);
    }

    /**
     * 查询我的订单列表
     * @param infoPage
     * @param userId
     * @return
     */
    @Override
    public Page<OrderInfo> getOrderList(Page<OrderInfo> infoPage, String userId) {
        //编写自定义SQL
        OrderInfoMapper orderInfoMapper = this.getBaseMapper();
        infoPage = orderInfoMapper.getOrderList(infoPage, userId);
        return infoPage;
    }

    /**
     * 将订单关闭
     * @param orderId
     */
    @Override
    public void execExpiredOrder(Long orderId) {
        this.updateOrderStatus(orderId, ProcessStatus.CLOSED);
    }

    /**
     * 根据订单ID修改为指定订单状态
     * @param orderId
     * @param closed
     */
    public void updateOrderStatus(Long orderId, ProcessStatus processStatus) {
        OrderInfo orderInfo = new OrderInfo();
        orderInfo.setId(orderId);
        orderInfo.setProcessStatus(processStatus.name());
        orderInfo.setOrderStatus(processStatus.getOrderStatus().name());
        this.updateById(orderInfo);
    }

    /**
     * 根据订单ID查询订单信息 包含订单明细
     * @param orderId
     * @return
     */
    @Override
    public OrderInfo getOrderInfoById(Long orderId) {
        //根据主键查询订单信息
        OrderInfo orderInfo = this.getById(orderId);
        if (orderInfo != null){
            //根据订单ID查询订单明细
            LambdaQueryWrapper<OrderDetail> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(OrderDetail::getOrderId,orderId);
            List<OrderDetail> orderDetailList = orderDetailService.list(queryWrapper);
            orderInfo.setOrderDetailList(orderDetailList);
            return orderInfo;
        }
        return null;
    }

    /**
     * 创建扣减库存消息,通知库存系统锁定库存
     * @param orderId
     */
    @Override
    public void sendDeductLockMsg(Long orderId) {
        //根据订单id查询订单明细
        OrderInfo orderInfo = this.getOrderInfoById(orderId);
        //将得到订单信息封装为库存系统所需要的Map对象
        if (orderInfo != null){
            Map stockMap = initWareMap(orderInfo);
            //发送扣减库存消息到MQ,通知库存系统锁定商品
            rabbitService.sendMessage(MqConst.EXCHANGE_DIRECT_WARE_STOCK,MqConst.ROUTING_WARE_STOCK, JSON.toJSONString(stockMap));
        }
    }

    /**
     * 提供给库存系统调用订单拆单接口
     * @param orderId 订单ID
     * @param wareSkuMap [{"wareId":"1","skuIds":["28","29"]},{"wareId":"2","skuIds":["24"]}]
     * 1.根据提交订单ID查询原始的订单以及订单明细
     * 2.遍历仓库跟商品SKU对应关系List 构建新的子订单 订单明细 进行保存
     * 3.更新原始订单状态：SPLIT
     * 4.按照接口文档构建响应结果[{orderId:1,wareId:"出货仓库ID",orderBody:"",details:[{},{}]},{}]
     * @return
     */
    @Override
    public String orderSplit(String orderId, String wareSkuMapStr) {
        //根据原始订单ID 查询原始订单信息以及原始订单明细信息
        if (StringUtils.isNotBlank(orderId) && StringUtils.isNotBlank(wareSkuMapStr)) {
            OrderInfo originOrderInfo = this.getOrderInfoById(Long.valueOf(orderId));
            if (originOrderInfo != null && !OrderStatus.SPLIT.name().equals(originOrderInfo.getOrderStatus())) {
                //原始订单订单明细
                List<OrderDetail> originOrderDetailList = originOrderInfo.getOrderDetailList();
                //将仓库ID 跟 商品ID 对照JSON字符串转为List集合-类型Map
                List<Map> allWareSkuMapList = JSON.parseArray(wareSkuMapStr, Map.class);
                if (!CollectionUtils.isEmpty(allWareSkuMapList)) {
                    //遍历集合每遍历一次产生子订单 得到子订单订单明细  将子订单跟订单明细新增
                    List<OrderInfo> allSubOrderInfoList = allWareSkuMapList.stream().map(wareSkuMap -> {
                        //新建子订单对象 大多数属性值来源于原始订单
                        OrderInfo subOrderInfo = new OrderInfo();
                        BeanUtils.copyProperties(originOrderInfo, subOrderInfo);
                        subOrderInfo.setId(null);//避免分布式ID出现主键冲突
                        subOrderInfo.setParentOrderId(originOrderInfo.getId());  //子订单关联原始订单
                        subOrderInfo.setUpdateTime(new Date());

                        //判断得到新子订单中订单明细集合
                        //获取到当前子订单中商品ID集合
                        List<String> skuIdStrList = (List<String>) wareSkuMap.get("skuIds");
                        //遍历原始订单明细集合将当前子订单中订单明细过滤出来
                        List<OrderDetail> newSubOrderDetailList = originOrderDetailList.stream().filter(originOrderDetail -> {
                            return skuIdStrList.contains(originOrderDetail.getSkuId().toString());
                        }).collect(Collectors.toList());
                        subOrderInfo.setOrderDetailList(newSubOrderDetailList);
                        subOrderInfo.sumTotalAmount();
                        //为新的子订单设置对应出货仓库ID
                        String wareId = (String) wareSkuMap.get("wareId");
                        subOrderInfo.setWareId(wareId);
                        //保存新子订单
                        this.save(subOrderInfo);
                        //todo 保存新订单订单明细
                        newSubOrderDetailList.stream().forEach(newSubOrderDetail -> {
                            //将新增子订单关联到订单明细
                            newSubOrderDetail.setOrderId(subOrderInfo.getId());
                        });
                        orderDetailService.saveBatch(newSubOrderDetailList);
                        return subOrderInfo;
                    }).collect(Collectors.toList());
                    //将原始订单状态改为 已拆单
                    originOrderInfo.setOrderStatus(OrderStatus.SPLIT.name());
                    this.updateById(originOrderInfo);

                    //按照接口文档响应结果给库存系统  TODO:保证每个订单包含对应出货仓库ID
                    List<Map> orderWareMap = allSubOrderInfoList.stream().map(subOrderInfo -> {
                        return this.initWareMap(subOrderInfo);
                    }).collect(Collectors.toList());
                    return JSON.toJSONString(orderWareMap);
                }
            }
        }
        return null;
    }

    /**
     * 根据订单ID查询订单以及订单明细
     * @param orderId
     * @return
     */
    public OrderInfo getOrderInfo(Long orderId) {
        //根据订单ID查询订单信息
        OrderInfo orderInfo = this.getById(orderId);
        //根据订单ID查询订单明细列表
        if (orderInfo!=null){
            LambdaQueryWrapper<OrderDetail> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(OrderDetail::getOrderId,orderId);
            List<OrderDetail> orderDetailList = orderDetailService.list(queryWrapper);
            orderInfo.setOrderDetailList(orderDetailList);
        }
        return orderInfo;
    }

    /**
     * 将订单信息封装为库存系统所需要Map集合对象
     * @param orderInfo
     * @return
     */
    private Map<String, Object> initWareMap(OrderInfo orderInfo) {
        HashMap<String, Object> map = new HashMap<>();
        //封装Map中订单信息
        map.put("orderId",orderInfo.getId());
        map.put("consignee",orderInfo.getConsignee());
        map.put("consigneeTel",orderInfo.getConsigneeTel());
        map.put("orderComment",orderInfo.getOrderComment());
        map.put("orderBody",orderInfo.getTradeBody());
        map.put("deliveryAddress",orderInfo.getDeliveryAddress());
        //orderInfo.getPaymentWay()==ALIPAY,库存系统1:在线支付
        map.put("paymentWay","1");
        //todo 新增,指定当前订单出货仓库ID
        if(StringUtils.isNotBlank(orderInfo.getWareId())){
            map.put("wareId",orderInfo.getWareId());
        }
        //封装Map中订单详情信息
        List<OrderDetail> orderDetailList = orderInfo.getOrderDetailList();
        if (!CollectionUtils.isEmpty(orderDetailList)){
            List<HashMap<String, Object>> details = orderDetailList.stream().map(orderDetail -> {
                HashMap<String, Object> detailMap = new HashMap<>();
                detailMap.put("skuId", orderDetail.getSkuId());
                detailMap.put("skuNum", orderDetail.getSkuNum());
                detailMap.put("skuName", orderDetail.getSkuName());
                return detailMap;
            }).collect(Collectors.toList());
            map.put("details",details);
        }
        return map;
    }


    /**
     * 保存订单信息
     * @param orderInfo
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    private Long saveOrderInfo(OrderInfo orderInfo) {
        //保存订单信息
        //为订单对象中部分属性赋值
        orderInfo.sumTotalAmount();//订单总金额
        orderInfo.setOrderStatus(OrderStatus.UNPAID.name());
        //生成订单唯一编号 改编号用于支付  TODO 采用雪花算法生成
        String outTradeNo = "SPH" + System.currentTimeMillis();
        orderInfo.setOutTradeNo(outTradeNo);
        List<OrderDetail> orderDetailList = orderInfo.getOrderDetailList();
        if (!CollectionUtils.isEmpty(orderDetailList)) {
            orderInfo.setImgUrl(orderDetailList.get(0).getImgUrl()); //默认订单显示商品图片
            String tradeBody = orderDetailList.stream()
                    .map(OrderDetail::getSkuName).collect(Collectors.joining(","));
            if (tradeBody.length() > 100) {
                tradeBody.substring(0, 100);
            }
            orderInfo.setTradeBody(tradeBody);
        }
        orderInfo.setOperateTime(new Date());
        Calendar calendar = Calendar.getInstance();
        calendar.add(Calendar.HOUR, 1);
        orderInfo.setExpireTime(calendar.getTime());
        orderInfo.setProcessStatus(ProcessStatus.UNPAID.name());
        //todo 从提交用户收件地址中获取  得到用户收件地址详细信息获取
        orderInfo.setProvinceId(1L);
        //执行订单保存
        this.save(orderInfo);
        Long orderId = orderInfo.getId();

        //保存订单明细信息
        orderDetailList.stream().forEach(orderDetail -> {
            orderDetail.setOrderId(orderId);
            //sourceId 1商城订单
            orderDetail.setSourceId(1L);
            orderDetail.setSourceType("MALL");
        });
        orderDetailService.saveBatch(orderDetailList);
        return orderId;
    }
}
